package drivers

import (
	"crypto/md5"
	"encoding/gob"
	"encoding/hex"
	"os"
	"path"
	"strings"
	"time"

	"gitee.com/yelhjt/gocache/config"
	"gitee.com/yelhjt/gocache/core"
)

// 本地缓存
type localCache struct {
	// 缓存
	*core.Cache
	// 配置
	config *config.LocalConfig
}

// 实例化
func NewLocal(conf *config.LocalConfig) core.CacheInterface {
	local_cache := &localCache{
		Cache: &core.Cache{
			// 不过期时间
			NoExpire: core.NoExpire,
			// 前缀
			Prefix: conf.Prefix,
			// 缓存数据
			Items: make(map[string]core.Item),
		},
		config: conf,
	}
	return local_cache
}

//----------------------------------
// 实现 CacheInterface 接口 start
//----------------------------------

/**
 * 获取值
 */
func (local_cache *localCache) Get(key string, defaultvalue interface{}) (interface{}, error) {
	// 获取文件路径
	value, err := local_cache.getRaw(key)
	// 如果文件不存在 或者 缓存过期，返回默认值
	if err == core.NoCacheFile || err == core.CacheExpire {
		return defaultvalue, nil
	}
	if err != nil {
		return nil, err
	}
	return value, nil
}

/**
 * 设置值
 */
func (local_cache *localCache) Set(key string, value interface{}, d time.Duration) bool {
	// 设置值
	_, errs := local_cache.set(key, value, d)
	if errs != nil {
		return false
	}
	return true
}

/**
 * 设置值 此名字则存在则报错
 */
func (local_cache *localCache) Add(key string, value interface{}, d time.Duration) error {
	// 获取这个值
	_, getrawerr := local_cache.getRaw(key)
	if getrawerr == nil {
		return core.CacheExist
	}
	// 设置值
	_, errs := local_cache.set(key, value, d)
	if errs != nil {
		return errs
	}

	return nil
}

/**
 * 修改值 ， 不存在则报错
 */
func (local_cache *localCache) Replace(key string, value interface{}, d time.Duration) error {
	// 获取这个值
	_, getrawerr := local_cache.getRaw(key)
	if getrawerr != nil {
		return getrawerr
	}
	// 设置值
	_, errs := local_cache.set(key, value, d)
	if errs != nil {
		return errs
	}
	return nil
}

/**
 * 清空所有缓存
 */
func (local_cache *localCache) Flush() {
	// 删除
	local_cache.SM.Lock()
	os.RemoveAll(local_cache.config.Path)
	local_cache.SM.Unlock()

}

/**
 * 清空单个缓存
 */
func (local_cache *localCache) Delete(key string) error {
	// 获取缓存文件路径
	filepath := local_cache.getCacheKey(key)
	// 检测文件是否存在
	_, err := os.Stat(filepath)
	if err != nil {
		// 文件不存在，输出
		return core.NoCacheFile
	}
	// 删除
	local_cache.delete(filepath)
	return nil
}

/**
 * 判断缓存是否存在
 */
func (local_cache *localCache) Has(key string) bool {
	// 获取数据
	_, err := local_cache.getRaw(key)
	if err != nil {
		return false
	}
	return true
}

//----------------------------------
// 实现 CacheInterface 接口 end
//----------------------------------

// 获取一个值
func (localCache *localCache) getRaw(key string) (interface{}, error) {
	// 获取缓存文件路径
	filepath := localCache.getCacheKey(key)
	// 检测文件是否存在
	_, err := os.Stat(filepath)
	if err != nil {
		// 文件不存在，输出
		return nil, core.NoCacheFile
	}
	// 打开文件
	f, err := os.Open(filepath)
	if err != nil {
		// 文件打开错误
		return nil, core.OpenCacheFileFail
	}
	defer f.Close()
	localCache.Cache.SM.RLock()
	// 创建一个gob解码器
	D := gob.NewDecoder(f)
	// 注册 gob  会出现的类型
	for _, v := range localCache.config.BogRegister {
		// gob注册类型，自定义的类型必须先注册
		gob.Register(v)
	}

	// 创建一个item 存储 gob解码出来的数据
	value := core.Item{}
	// 解码
	err = D.Decode(&value)
	if err != nil {
		return nil, err
	}
	// 解锁
	localCache.SM.RUnlock()
	// 关闭
	f.Close()
	// 有value
	if value.Expire != int64(localCache.NoExpire) {
		// 有过期时间
		if value.Expire < time.Now().UnixNano() {
			// 过期 删除文件
			localCache.delete(filepath)
			return nil, core.CacheExpire
		}
	}
	return value.Value, nil
}

// 过期删除
func (localCache *localCache) delete(filepath string) {
	localCache.SM.Lock()
	removeFile(filepath)
	localCache.SM.Unlock()
}

// 获取cacheKey
func (local_cache *localCache) getCacheKey(key string) string {
	// MD5 加密
	h := md5.New()
	h.Write([]byte(key))
	md5 := hex.EncodeToString(h.Sum([]byte(local_cache.Prefix)))
	// 子目录
	filepath := md5[0:2] + "/" + md5[2:] + ".gob"
	if local_cache.Prefix != "" {
		filepath = local_cache.Prefix + "/" + md5[0:2] + "/" + local_cache.Prefix + md5[2:] + ".gob"
	}
	return local_cache.config.Path + "/" + filepath
}

// 设置
func (local_cache *localCache) set(key string, value interface{}, d time.Duration) (bool, error) {
	// 获取key的缓存目录
	filepath := local_cache.getCacheKey(key)
	// 文件夹不存在，创建文件夹
	filepath_arr := strings.Split(filepath, "/") // 根据“/” 截断
	// 获取数组程度
	length := len(filepath_arr)
	// 判断文件是否存在
	_, err := os.Stat(filepath)
	if err != nil {
		// 文件可能
		// 判断文件夹是否存在
		if os.IsNotExist(err) {
			// 文件夹路径
			path := strings.Join(filepath_arr[:length-1], "/")
			err = os.MkdirAll(path, os.ModePerm)
			if err != nil {
				return false, err
			}
		} else {
			return false, err
		}
	}

	// 过期时间设置
	var expire int64 = 0
	if d == local_cache.NoExpire {
		expire = 0
	} else {
		expire = time.Now().Add(d).UnixNano()
	}

	// item对象
	it := core.Item{
		Value:  value,
		Expire: expire,
	}

	// 打开文件
	fs, _ := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, 0777)
	defer fs.Close()
	// 加 互斥锁
	local_cache.SM.Lock()
	// 解 互斥锁
	defer local_cache.SM.Unlock()
	//写
	enc := gob.NewEncoder(fs)
	// 这里必须注册value的类型
	gob.Register(value)
	// 注册 gob  会出现的类型
	for _, v := range local_cache.config.BogRegister {
		// gob注册类型，自定义的类型必须先注册
		gob.Register(v)
	}

	if err := enc.Encode(it); err != nil {
		return false, err
	}
	return true, nil
}

func removeFile(filepath string) {
	err := os.Remove(filepath)
	if err == nil {
		// 删除成功，再看看文件夹是否为空，为空接着删除
		pathtemp, _ := path.Split(filepath)
		removeFile(pathtemp[:len(pathtemp)-1])
	}
}
